package com.detrum.sqlite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by abc on 2018/4/13.
 */

public class SqliteOperate {
    private final String TAG = "lsyUtils";
    private static SqliteOperate sqliteOperate;
    private static SQLiteDatabase sqLiteDatabaseWrite, sqLiteDatabaseRead;

    public SqliteOperate(Context context) {
        sqLiteDatabaseWrite = SqliteHelper.getInstance(context).getWritableDatabase();
        sqLiteDatabaseRead = SqliteHelper.getInstance(context).getReadableDatabase();
    }

    public static SqliteOperate getInstence(Context context) {
        if (sqliteOperate == null) {
            sqliteOperate = new SqliteOperate(context);
        }
        synchronized (sqliteOperate) {
            return sqliteOperate;
        }
    }

    /**
     * 插入一条数据，必须在之前已创建对应的表，如已存在指定的值name则更新对应的数据。
     * 如果没有name字段则无法判断是否已存在相同数据
     *
     * @param obj  插入的数据对象
     * @return     返回插入后的结果 小于0的值代表插入失败
     */
    public long Insert(Object obj) {
        long insert = -1;
        String tableName = obj.getClass().getSimpleName();
        Field[] fields = obj.getClass().getDeclaredFields();
        Method[] rMethod = getReturnMethod(obj);
        if (rMethod.length != fields.length) {
            Log.e(TAG, "SqliteOperate obj method and field is not matching");
            return -2;
        }
        Cursor cursor = null;
        String name = null;
        for (int i = 0; i < rMethod.length; i++) {
            String rMethodName = rMethod[i].getName().substring(3).toLowerCase();
            if (rMethodName.equals("name")) {
                try {
                    name = (String) rMethod[i].invoke(obj);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    return -4;
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                    return -3;
                }
                cursor = sqLiteDatabaseRead.query(tableName, null, fields[0].getName() + " = ?", new String[]{name}, null, null, null);
            }
        }
        if (cursor != null && cursor.getCount() > 0) {
            return UpdataInsert(obj, name);
        }
        ContentValues values = getReturnValues(obj, rMethod);
        if (values == null) {
            return -5;
        }
        insert = sqLiteDatabaseWrite.insert(tableName, null, values);
        return insert;
    }

    private int UpdataInsert(Object obj, String name) {
        int updata = -1;
        String tableName = obj.getClass().getSimpleName();
        Method[] rMethod = getReturnMethod(obj);
        ContentValues values = getReturnValues(obj, rMethod);
        if (values == null) {
            return -5;
        }
        updata = sqLiteDatabaseWrite.update(tableName, values, "name = ?", new String[]{name});
        Log.e(TAG, "update = " + updata);
        return updata;
    }

    public List<? extends Object> SelectAll(Class tClass) {
        List beanList = new ArrayList<>();
        String tableName = tClass.getSimpleName();
        Cursor cursor = sqLiteDatabaseRead.query(tableName, null, null, null, null, null, null);
        int count = cursor.getCount();
        if (count == 0) {
            return beanList;
        }
        String[] columns = cursor.getColumnNames();
        if (columns == null) {
            return beanList;
        }
        if (columns.length == 0) {
            return beanList;
        }
        Method[] vMethod = getSetMethod(tClass);
        while (cursor.moveToNext()) {
            beanList.add(getSingleObject(tClass,vMethod,cursor));
        }
        cursor.close();
        return beanList;
    }

    public List<? extends Object> SelectPage(Class tClass,String key,int page){
        List beanList = new ArrayList<>();
        String tableName = tClass.getSimpleName();
        Cursor cursor = sqLiteDatabaseRead.query(tableName, null, key+" > "+page, null, null, null, null,"10");
        int count = cursor.getCount();
        if (count == 0) {
            return beanList;
        }
        String[] columns = cursor.getColumnNames();
        if (columns == null) {
            return beanList;
        }
        if (columns.length == 0) {
            return beanList;
        }
        Method[] vMethod = getSetMethod(tClass);
        while (cursor.moveToNext()) {
            beanList.add(getSingleObject(tClass,vMethod,cursor));
        }
        cursor.close();
        return beanList;
    }

    public Object SelectSingle(Class tClass,String key,String value){
        String tableName = tClass.getSimpleName();
        Method[]vMethod = getSetMethod(tClass);
        Cursor cursor = sqLiteDatabaseRead.query(tableName,null,key+"=?",new String[]{value},null,null,null);
        if (cursor!=null&&cursor.moveToNext()) {
            return getSingleObject(tClass,vMethod,cursor);
        }else{
            return null;
        }
    }

    private Method[] getReturnMethod(Object obj) {
        Method[] methods = obj.getClass().getDeclaredMethods();
        ArrayList<Method> rList = new ArrayList<>();
        for (int i = 0; i < methods.length; i++) {
            String returnName = methods[i].getReturnType().getSimpleName();
            if (!returnName.equals("void")) {
                rList.add(methods[i]);
            }
        }
        Method[] rMethod = new Method[rList.size()];
        rMethod = rList.toArray(rMethod);
        return rMethod;
    }

    private ContentValues getReturnValues(Object obj, Method[] rMethod) {
        ContentValues values = new ContentValues();
        for (int i = 0; i < rMethod.length; i++) {
            String rMethodName = rMethod[i].getName();
            if (rMethodName.startsWith("get")) {
                rMethodName = rMethodName.substring(3).toLowerCase();
            }
            try {
                String typeName = rMethod[i].getReturnType().getSimpleName();
                if (typeName.equals("String")) {
                    String str = (String) (rMethod[i].invoke(obj));
                    values.put(rMethodName, str);
                } else if (typeName.equals("int")) {
                    int id = (int) (rMethod[i].invoke(obj));
                    values.put(rMethodName, id);
                } else if (typeName.equals("long")) {
                    long timestamp = (long) (rMethod[i].invoke(obj));
                    values.put(rMethodName, timestamp);
                } else if (typeName.equals("double")) {
                    double price = (double) (rMethod[i].invoke(obj));
                    values.put(rMethodName, price);
                } else if (typeName.equals("byte[]")) {
                    byte[] data = (byte[]) (rMethod[i].invoke(obj));
                    values.put(rMethodName, data);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                return null;
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                return null;
            }
        }
        return values;
    }

    private Method[] getSetMethod(Class tClass) {
        Method[] methods = tClass.getDeclaredMethods();
        ArrayList<Method> vList = new ArrayList<>();
        for (int i = 0; i < methods.length; i++) {
            String returnName = methods[i].getReturnType().getSimpleName();
            if (returnName.equals("void")) {
                vList.add(methods[i]);
            }
        }
        Method[] vMethod = new Method[vList.size()];
        vMethod = vList.toArray(vMethod);
        return vMethod;
    }

    private Object getSingleObject(Class tClass,Method[] vMethod,Cursor cursor){
        Object obj = null;
        try {
            obj = tClass.newInstance();
            for (int i = 0; i < vMethod.length; i++) {
                String parameter = vMethod[i].getParameterTypes()[0].getSimpleName();
                String methodName = vMethod[i].getName().substring(3).toLowerCase();
                if (parameter.equals("int")) {
                    int index = cursor.getColumnIndex(methodName);
                    int a = cursor.getInt(index);
                    vMethod[i].invoke(obj, a);
                } else if (parameter.equals("String")) {
                    String b = cursor.getString(cursor.getColumnIndex(methodName));
                    Log.e(TAG, "name = " + b);
                    vMethod[i].invoke(obj, b);
                } else if (parameter.equals("double")) {
                    double c = cursor.getDouble(cursor.getColumnIndex(methodName));
                    vMethod[i].invoke(obj, c);
                } else if (parameter.equals("long")) {
                    long d = cursor.getLong(cursor.getColumnIndex(methodName));
                    vMethod[i].invoke(obj, d);
                } else if (parameter.equals("byte[]")) {
                    byte[] e = cursor.getBlob(cursor.getColumnIndex(methodName));
                    vMethod[i].invoke(obj, e);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

}

